/*
 * MIT License
 *
 * Copyright (c) 2024-2025 milkpotatoes
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#ifndef SRC_INCLUDE_NAPI_RUNNER_NAPI_RUNNER_H
#define SRC_INCLUDE_NAPI_RUNNER_NAPI_RUNNER_H

#include <string>
#include <string_view>
#include <unordered_map>

#include <ecmascript/napi/include/jsnapi_expo.h>
#include <napi/native_api.h>
#include <uv.h>

#include "runner_utils/log.h"

using JSNApi = panda::JSNApi;
namespace panda::ecmascript
{
class JSRuntimeOptions;
}
class JsTimer;
class ArkNativeEngine;
class NativeModule;

class NapiRunner
{
public:
    explicit NapiRunner(panda::ecmascript::JSRuntimeOptions *runtimeOptions);

    ~NapiRunner();

    int InitLoop();
    void InitNativeModule();
    void LoadTSModule();
    int RunScript(const std::string &path);
    inline int RunScript(const std::string_view &path)
    {
        return RunScript(std::string(path));
    }

    int RunLoop(uv_run_mode mode);

    inline ArkNativeEngine *GetEngine() const
    {
        return env_;
    }

    inline napi_env GetEnv() const
    {
        return reinterpret_cast<napi_env>(env_);
    }

    inline panda::ecmascript::EcmaVM *GetVM() const
    {
        return vm_;
    }

    inline ArkNativeEngine *operator->() const
    {
        return env_;
    }

    static void InitProcessArgs(int argc, const char **args, int index);
    static void SetNApiLogLevel(RunnerLogLevel level);

private:
    static panda::Local<panda::JSValueRef> RequireNapi(panda::JsiRuntimeCallInfo *info);
    static panda::Local<panda::JSValueRef> PreLoad(panda::JsiRuntimeCallInfo *info);
    static panda::Local<panda::JSValueRef> InnerExecute(panda::JsiRuntimeCallInfo *info);

    constexpr static std::string_view PANDA_MAIN_FUNCTION{"_GLOBAL::func_main_0"};

    panda::ecmascript::EcmaVM *vm_{nullptr};
    ArkNativeEngine *env_{nullptr};
    JsTimer *timer_{nullptr};
    std::unordered_map<NativeModule *, panda::Global<panda::JSValueRef>> loadedModules_{};
    uv_check_t checkHandle_;

    static int processArgc_;
    static int scriptArgIndex_;
    static const char **processArgs_;
};
#endif /* SRC_INCLUDE_NAPI_RUNNER_NAPI_RUNNER_H */
